<?php
/**
 * 梦幻科技团队
 * from 2013
 * QQ: 523994553
 */
/*********
注意:
	1, link->CSS和Img标签要加上路径前缀:	{{$smarty.const.__APP__}}
	2, 如某个php链接不想被转换成html静态模式,加上__ROOT__即可 (例:<a href="{{$smarty.const.__ROOT__}}index.php?app=a&act=b"></a>)
	3, php代码中将一个源生url转换为静态url: Router::encodeUrl('index.php?app=config&act=modify');
	4, 模板中将一个源生url转换为静态URL: {{'index.php?app=config&act=modify'|URL}}
	5, form标签中的action,可以识别action="index.php?app=xxx"(注:url为index.php时要去掉留空或者index.php?加一问号也行)
/*********/

class Router {

	/**
	 * 实例化
	 * @return Router
	 */
	public static function getInstance(){
		$Router = new Router();
		return $Router;
	}

	/**
	 * encode一条URL  动态->静态
	 * @param string $url 动态url
	 * PHP: Router::encodeUrl('index.php?app=config&act=modify');
	 * 模板: {{'index.php?app=config&act=modify'|URL}}
	 */
	public static function encodeUrl($url=''){
		//没开户rewrite配置时
		if(!C('open_rewrite'))
			return $url;
		//url字符串不存在index.php?前缀时，直接返回
		if(substr($url,0,10)!='index.php?'){
			return $url;
		}
		//将&amp;字符串替换为&
		$url = str_replace('&amp;','&',$url);
		//将字符串：index.php? 去掉
		$url = str_replace('index.php?','',$url);
		//提取锚点#后缀到$suffix变量
		if(strpos($url,'#')!==false){
			list($url,$suffix) = explode('#',$url); 
		}
		$url = trim($url);
		if(!$url)
			return $url;
		//将{{}}里边的smarty模板语法编码urlencode()，避免后面的处理过程中被破坏(主要针对这两种字符：&、=)
		if(!function_exists('__temp1__')){ function __temp1__($str){ return '{{'.urlencode($str[1]).'}}'; } }
		$url = preg_replace_callback( '/{{(.*)}}/mU', '__temp1__', $url);
		//将URL字符串以&分隔成数组
		$queryParts = explode('&', $url);
		foreach ($queryParts as $key=>&$param){
			//排除app参数
			if(substr($param,0,4)=='app='){
				$app = str_replace('app=','',$param);
				unset($queryParts[$key]);
			}
			//排除act参数
			elseif(substr($param,0,4)=='act='){
				$act = str_replace('act=','',$param);
				unset($queryParts[$key]);
			}
			//其它参数：将第一个等号换成-符
			elseif(($num=strpos($param,"="))!==FALSE){
				$param{$num} = '-';
			}
		}
		//将解释好的数组重新用/分隔组合成URL字符串
		$url = implode('/',$queryParts);
		//将app,act参数放到url前面
		$url = $app.(!empty($act)?'/'.$act:'').(!empty($url)?'/'.$url:'');
		//将smarty模板语法{{}}里边的代码解码：urldecode()
		if(!function_exists('__temp2__')){ function __temp2__($str){ return '{{'.urldecode($str[1]).'}}'; } }
		$url = preg_replace_callback( '/{{(.*)}}/mU', '__temp2__', $url);
		//路由规则自定义(如：'case/cat-'=>'cases/c')
		if(is_array(C('ROUTER_RULE')) AND count(C('ROUTER_RULE'))>0){
			foreach(C('ROUTER_RULE') as $key=>$value){
				if(strpos($url,$key)!==false)
					$url = str_replace($key,$value,$url);
			}
		}
		//添加后缀
		if(C('URL_MODEL')=='html')//html模式
			$url = __ROOT__.$url.'.html';
		if(C('URL_MODEL')=='dir')//路径模式
			$url = __ROOT__.$url.'/';
		//恢复锚点#后缀
		if(!empty($suffix)!='')
			$url .= '#'.$suffix;
		return $url;
	}

	/**
	 * encode整个模板页面html所有的URL  动态->静态（用于smarty编译）
	 */
	public static function encodeUrls($content=''){
		if($content=='') return false;
		//没有开启rewrite时直接返回
		if(!C('open_rewrite'))
			return $content;
		//转换一条url函数  php原始url->静态url
		if(!function_exists('dealOneUrl')){
			function dealOneUrl($u){ return $u[1].Router::encodeUrl($u[2]).$u[3]; }
		}
		// ((?:href|link|action)=[\"\'])(index.php\? 》匹配 href或link或action + 双引号或单引号 + 字符:index.php?
		// (?:[^\'\"]*{{[^}]*}})*(?:[^\'\"]*)) 》匹配 (非单双引号开头的字符集 + 双中括号字符串{{...}})匹配前面内容的0次1次或多次 + 非单双引号字符集、可以是0次或多次
		// ([\'\"]) 》匹配 单引号或双引号收尾
		// m: 多行识别
		$content = preg_replace_callback('/((?:href|link|action)=[\"\'])(index.php\?(?:[^\'\"]*{{[^}]*}})*(?:[^\'\"]*))([\'\"])/m', 'dealOneUrl', $content);
		return $content;
	}

	/**
	 * 解释当前访问的静态URL  静态->动态
	 * @param string $url 静态网址(为空时默认为当前网址)
	 * @param array $otherParam 额外参数（也可以用于参数的覆盖，如分页类参数:page）
	 */
	public static function decodeUrl($url='',$otherParam=array()){
		//为空时解释当前网址
		if(empty($url)){
			$url = $_SERVER["QUERY_STRING"];
			$current = true;
		}
		//以&/?分割 (分割地址中同时可能存在的: 拟静态URL 和 原生URL)//如$url='listing/school&page=2' //nginx以?分隔, apache以?分隔
		$symbol = My::is_nginx()?'&':'?';
		if(strpos($url,$symbol)!==false){
			$query = strstr($url,$symbol);//取原生URL部分 (取第一次&出现至最尾的字符)
			$query = trim($query,$symbol);//去除两边?号或&号
			$temp = explode($symbol,$url);
			$url = $temp[0]; //将$url字符串explode成数组，并只取最前面第一个
		}
		//url中不存在?号时，证明是这种url方式http://xxx/?xxx，所以直接将$url置空
		elseif(My::is_apache()){
			$url = '';
		}
		//将?号后面的参数解释->合并到$_REQUEST参数中
		if(isset($query)) { 
			parse_str($query,$urlArr);
		}
		//去除后缀
		$url = My::rtrim($url,'.html');
		$url = rtrim($url,'/');
		$url = rtrim($url,'?');
		//路由规则自定义
		if(is_array(C('ROUTER_RULE')) AND count(C('ROUTER_RULE'))>0){
			foreach(C('ROUTER_RULE') as $key=>$value){
				if(strpos($url,$value)!==false)
					$url = str_replace($value,$key,$url);
			}
		}
		//当前Url为原生url时
		if(substr_count($url,'app=')>0 || substr_count($url,"?")>0 ) { return 'index.php?'.$_SERVER["QUERY_STRING"]; }
		//过滤两边"/"
		$url=trim($url,'/');
		$arr=explode('/',$url);
		foreach($arr as $key=>$value){
			if(strstr($value,"-")){
				$var_key = substr($value,0,strpos($value,"-"));//确定key的值
				$var_val = substr(strstr($value,"-"),1);//确定变量的值
				$urlArr[$var_key] = ($var_val);//urldecode rawurlencode urlencode
			}else{
				if($key==0) $urlArr['app']=$value;
				elseif($key==1) $urlArr['act']=$value;
			}
		}
		//有额外参数时，将当前url的参数与otherParam合在一起（当前重复参数名时，后面的参数会覆盖前面的同名参数）
		$urlArr = (is_array($otherParam) AND $otherParam) ? array_merge($urlArr,$otherParam) : $urlArr;
		//合并到get/request
		if(isset($current) AND $current==true){
			$_GET=array_merge($urlArr,$_GET);
			$_REQUEST=array_merge($urlArr,$_REQUEST);
		}
		//返回url
		return 'index.php?'.http_build_query($urlArr);
	}



	####################################################################
	##################### 抽出body中的css/js代码 S #####################
	####################################################################
	/**
	 * 抽出body中的css/js代码
	 * 注意1:body中不能存在注释掉的<script标签
	 * js代码一定要放;号结束语句
	 * 不能出现多个</head>
	 */
	public function body_filter($content=''){
		//抽出<!--[if lte IE 6]><![endif]-->
		$content = preg_replace_callback('/<!--\[if[^>]*?>[\s\S]*?<!\[endif\]-->/mi', array('self','_get_body_filter'), $content);
		//抽出<link>
		$content = preg_replace_callback('/<link.*([^>]*)>/mi', array('self','_get_body_filter'), $content);
		//抽出<style>
		$content = preg_replace_callback('/<style[^>]*?>[\s\S]*?<\/style>/mi', array('self','_get_body_filter'), $content);
		//抽出<script>
		$content = preg_replace_callback('/<script[^>]*?>[\s\S]*?<\/script>/mi', array('self','_get_body_filter'), $content);
		//去除html中的注释<!---->
		$content=preg_replace('/<!--.*?-->/s','',$content);
		//过滤去掉js/css中的:换行和tab
		$content = str_replace(array("\r\n","\n",'	')," ",$content);
		//干掉js/css代码中的注释
		$this->filter_html = self::_filter_note($this->filter_html);
		//过滤去掉js/css中的:换行和tab
		$this->filter_html = str_replace(array("\r\n","\n",'	'),"",$this->filter_html);
		//将js和css全部放到head
		$content = str_replace('</head>',$this->filter_html."\r\n</head>",$content);
		//return
		return $content;
	}
	//过滤js中的两种注释// 和/**/  (参数全为js/css标签相关代码)
	private function _filter_note($content){
		$content=preg_replace('/http:\/\//','<|HTTP|>',$content);//因为url中有http://所以先替换掉
		$content=preg_replace('/\/\*(.*?)\*\//is','',$content);
		$content=preg_replace('/\/\/(.*)/','',$content);
		$content=preg_replace('/\<\|HTTP\|\>/','http://',$content);//重新替换回url中的http://
		return $content;
	}
	//抽出body中的<script>标签
	private function _get_body_filter($str){
		$this->filter_html .= $str[0];
		return '';
	}
	####################################################################
	##################### 抽出body中的css/js代码 E #####################
	####################################################################

}